﻿using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ESRI.ArcGIS.Client.Geometry;
using ESRI.ArcGIS.Client.Tasks;

namespace ESRI.ArcGIS.Client.Async
{
    public static partial class GeometryServiceExtensions
    {
        #region AreasAndLengths

        /// <summary>
        /// Calculates the areas and lengths of each specified polygon.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate areas and lengths.</param>
        /// <param name="lengthUnit">The length unit in which perimeters of polygons will be calculated.
        /// If unit is not specified, the units are derived from the spatial reference of the first geometry.</param>
        /// <param name="areaUnit">The area unit in which areas of polygons will be calculated.
        /// If unit is not specified, the units are derived from the spatial reference of the first geometry.</param>
        /// <param name="calculationType">The type of calculation to be preformed. e.g.(Planar,Geodesic,PreserveShape)</param>
        /// <returns></returns>
        public static Task<AreasAndLengthsTaskResult> AreasAndLengthsTaskAsync(this GeometryService task,
            IList<Graphic> graphics, LinearUnit? lengthUnit, LinearUnit? areaUnit, CalculationType calculationType)
        {
            return AreasAndLengthsTaskAsync(task, graphics, lengthUnit, areaUnit, calculationType,
                CancellationToken.None);
        }

        /// <summary>
        /// Calculates the areas and lengths of each specified polygon.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate areas and lengths.</param>
        /// <param name="lengthUnit">The length unit in which perimeters of polygons will be calculated.
        /// If unit is not specified, the units are derived from the spatial reference of the first geometry.</param>
        /// <param name="areaUnit">The area unit in which areas of polygons will be calculated.
        /// If unit is not specified, the units are derived from the spatial reference of the first geometry.</param>
        /// <param name="calculationType">The type of calculation to be preformed. e.g.(Planar,Geodesic,PreserveShape)</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<AreasAndLengthsTaskResult> AreasAndLengthsTaskAsync(this GeometryService task,
            IList<Graphic> graphics, LinearUnit? lengthUnit, LinearUnit? areaUnit, CalculationType calculationType,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, AreasAndLengthsEventArgs, AreasAndLengthsTaskResult>(
                t => t.AreasAndLengthsAsync(graphics, lengthUnit, areaUnit, calculationType),
                (t, e) => t.AreasAndLengthsCompleted += e,
                (t, e) => t.AreasAndLengthsCompleted -= e,
                e => new AreasAndLengthsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Lengths

        /// <summary>
        /// Calculates the lengths of each specified polyline.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate lengths.</param>
        /// <param name="lengthUnit">The length unit in which perimeters of polygons will be calculated.
        /// If unit is not specified, the units are derived from the Spatial Reference of the geometry.</param>
        /// <param name="calculationType">The type of calculation to perform. e.g.(Planar,Geodesic,PreserveShape).
        /// CalculationType.Planar is supported in ArcGIS Server v9.x+ services.
        /// CalculationType.Geodesic is supported in ArcGIS Server v10.0+ services.
        /// CalculationType.PreseveShape is supported in ArcGIS Server v10.1+ services.</param>
        /// <returns></returns>
        public static Task<LengthsTaskResult> LengthsTaskAsync(this GeometryService task, IList<Graphic> graphics,
            LinearUnit? lengthUnit, CalculationType calculationType)
        {
            return LengthsTaskAsync(task, graphics, lengthUnit, calculationType, CancellationToken.None);
        }

        /// <summary>
        /// Calculates the lengths of each specified polyline.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate lengths.</param>
        /// <param name="lengthUnit">The length unit in which perimeters of polygons will be calculated.
        /// If unit is not specified, the units are derived from the Spatial Reference of the geometry.</param>
        /// <param name="calculationType">The type of calculation to perform. e.g.(Planar,Geodesic,PreserveShape).
        /// CalculationType.Planar is supported in ArcGIS Server v9.x+ services.
        /// CalculationType.Geodesic is supported in ArcGIS Server v10.0+ services.
        /// CalculationType.PreseveShape is supported in ArcGIS Server v10.1+ services.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<LengthsTaskResult> LengthsTaskAsync(this GeometryService task, IList<Graphic> graphics,
            LinearUnit? lengthUnit, CalculationType calculationType, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, LengthsEventArgs, LengthsTaskResult>(
                t => t.LengthsAsync(graphics, lengthUnit, calculationType),
                (t, e) => t.LengthsCompleted += e,
                (t, e) => t.LengthsCompleted -= e,
                e => new LengthsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Project

        /// <summary>
        /// Projects a set of Graphics into a new SpatialReference including a DatumTransform.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics containing the geometry to project.</param>
        /// <param name="outSpatialReference">The SpatialReference to project to.</param>
        /// <param name="datumTransform">The datum transformation object.
        /// Support for DatumTransfom works with ArcGIS Server 10.1+</param>
        /// <param name="transformForward">If DatumTransform is provided transformFoward value will indicate to
        /// project datum forward if true or backwards if false. Support for DatumTransfom works with ArcGIS Server 10.1+</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> ProjectTaskAsync(this GeometryService task, IList<Graphic> graphics,
            SpatialReference outSpatialReference, DatumTransform datumTransform, bool transformForward = false)
        {
            return ProjectTaskAsync(task, graphics, outSpatialReference, datumTransform, transformForward,
                CancellationToken.None);
        }

        /// <summary>
        /// Projects a set of Graphics into a new SpatialReference including a DatumTransform.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics containing the geometry to project.</param>
        /// <param name="outSpatialReference">The SpatialReference to project to.</param>
        /// <param name="datumTransform">The datum transformation object.
        /// Support for DatumTransfom works with ArcGIS Server 10.1+</param>
        /// <param name="transformForward">If DatumTransform is provided transformFoward value will indicate to
        /// project datum forward if true or backwards if false. Support for DatumTransfom works with ArcGIS Server 10.1+</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> ProjectTaskAsync(this GeometryService task, IList<Graphic> graphics,
            SpatialReference outSpatialReference, DatumTransform datumTransform, bool transformForward,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.ProjectAsync(graphics, outSpatialReference, datumTransform, transformForward),
                (t, e) => t.ProjectCompleted += e,
                (t, e) => t.ProjectCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

    }
}


